package com.vivi.codereview;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.vivi.codereview.actors.AgileDeveloperActor;
import com.vivi.codereview.actors.AgileReviewerActor;
import com.vivi.codereview.actors.CheckinCheckerActor;
import com.vivi.codereview.actors.DeveloperActor;
import com.vivi.codereview.actors.IActor;
import com.vivi.codereview.actors.ReviewerActor;
import com.vivi.codereview.actors.TestAgileReviewerActor;
import com.vivi.codereview.actors.UnhijackerActor;
import com.vivi.codereview.displayer.Displayer;
import com.vivi.codereview.displayer.Displayer.DisplayerEnum;
import com.vivi.codereview.tools.Log;
import com.vivi.codereview.tools.UserInputHandler;

/**
 *
 * Main launcher of CodeReviewTool. This class handle the arguments you can
 * provide to the application, the selection of the actor to be launched, and
 * the log management in case of big issue.
 *
 * @author vivi
 *
 */
public class CodeReviewTool {

  /**
   * Argument to activate debug mode.
   */
  private static final String DEBUG = "-d";

  /**
   * Argument to activate deep debug mode.
   */
  private static final String DEEP_DEBUG = "-dd";

  /**
   * Argument to activate manual modes.
   */
  private static final String MANUAL_PROCESS = "-o";

  /**
   * Argument to activate undo hijack mode.
   */
  private static final String UNHIJACK = "-u";

  /**
   * Argument to activate checkin checker mode.
   */
  private static final String CHECKIN_CHECKER = "-c";

  /**
   * Allow tester mode.
   */
  private static final String TESTER = "-t";

  /**
   * Allow no junit mode.
   */
  private static final String NO_JUNIT = "-nojunit";

  /**
   * Display release note.
   */
  private static final String VERSION = "-v";

  /**
   * Display help.
   */
  private static final String HELP = "-h";

  /**
   * Do the manual actors are activated?
   */
  private static boolean isManualReviewProcessEnabled;

  /**
   * Does the undo hijack actor is activated?
   */
  private static boolean isUndoHijackModeEnabled;

  /**
   * Does the checkin checker actor is activated?
   */
  private static boolean isCheckinCheckerModeEnabled;

  /**
   * Does the tester actors are enabled?
   */
  private static boolean isTesterModeEnabled;

  /**
   * Do we allow creation of zip without junit?
   */
  private static boolean isAllowingNoJunit;

  private static boolean displayRelease;

  private static boolean displayHelp;

  /**
   * main method
   *
   * @param args
   *          the args
   */
  public static void main(String[] args) {
    CodeReviewTool.checkArgs(args);
    int result = CodeReviewTool.start(args);
    System.exit(result);
  }

  /**
   * Handle all the arguments provided to the application
   *
   * @param args
   *          the args
   */
  private static void checkArgs(String[] args) {
    if (args != null) {
      List<String> argv = Arrays.asList(args);
      if (argv.contains(DEBUG)) {
        Log.activateDebugMode();
      }
      if (argv.contains(DEEP_DEBUG)) {
        Log.activateDeepDebugMode();
      }
      if (argv.contains(MANUAL_PROCESS)) {
        isManualReviewProcessEnabled = true;
      }
      if (argv.contains(UNHIJACK)) {
        isUndoHijackModeEnabled = true;
      }
      if (argv.contains(CHECKIN_CHECKER)) {
        isCheckinCheckerModeEnabled = true;
      }
      if (argv.contains(TESTER)) {
        isTesterModeEnabled = true;
      }
      if (argv.contains(NO_JUNIT)) {
        isAllowingNoJunit = true;
      }
      if (argv.contains(VERSION)) {
        displayRelease = true;
      }
      if (argv.contains(HELP)) {
        displayHelp = true;
      }
    }
  }

  /**
   * Only this program can instanciate this class
   */
  private CodeReviewTool() {
    super();
  }

  /**
   * The main process, letting the user to choose the actor, launching it, and
   * handling any fatal exception.
   *
   * @param args
   *          the args
   * @return the exit code (0 when success)
   */
  public static int start(String[] args) {
    Log.log("We start execution", Log.I);
    Log.log("We are called with this command line: " + Arrays.asList(args).toString(), Log.D);
    int result = preProcess();
    if (result == Log.Exit.CONTINUE) {
      result = process();
    }
    return result;
  }

  /**
   * Some preprocesses, like displaying release and / or help
   *
   * @return
   */
  protected static int preProcess() {
    int result = Log.Exit.CONTINUE;
    try {
      if (displayHelp) {
        result = new Displayer(DisplayerEnum.Help).display();
      }
      if (displayRelease) {
        result = new Displayer(DisplayerEnum.Release).display();
      }
    }
    catch (WhatTheFuckException wtfe) {
      result = catchException(wtfe);
    }
    catch (Exception e) {
      result = catchException(e);
    }
    return result;
  }

  /**
   * Start the main process, calling the right actor.
   *
   * @return
   */
  protected static int process() {
    IActor actor = null;
    int result;
    try {
      actor = getCodeReviewActor();
      Log.log("We are called for this actor " + actor.getClass().getCanonicalName(), Log.D);
      result = actor.process();
      if (result == Log.Exit.OK) {
        Log.log("Job completed", Log.I);
      }
      else {
        Log.log("Job completed with result=" + result, Log.W);
      }
    }
    catch (WhatTheFuckException wtfe) {
      result = catchException(wtfe);
    }
    catch (Exception e) {
      result = catchException(e);
    }
    return result;
  }

  protected static int catchException(WhatTheFuckException wtfe) {
    int result = wtfe.getValue();
    Log.log("Oups, something bad happened, consider the logs (" + result + ").", Log.E);
    logFile();
    return result;
  }

  protected static int catchException(Exception e) {
    Log.log("Oups, something bad happened: " + e.getMessage(), Log.E);
    int result = Log.Exit.GENERIC_ERROR;
    logFile();
    return result;
  }

  protected static void logFile() {
    Log.log("Please provide all those logs to Nicolas Villard-Parriault", Log.E);
    Log.log("A file has been saved in " + Log.getLogFileLocation(), Log.E);
    Log.saveLogFile();
  }

  /**
   * Ask to the user which actor he wants to use
   *
   * @return the actor to be launched
   * @throws Exception
   */
  private static IActor getCodeReviewActor() throws Exception {
    Log.log("Select between:", Log.I);
    Map<String, IActor> actors = getActors();
    String selectedActor = new UserInputHandler().letUserChooseOne(actors.keySet(), "Select your actor:", false);
    Log.log(selectedActor + " has been selected", Log.D);
    return actors.get(selectedActor);
  }

  /**
   * Return a Map with all available actors.
   *
   * @return a Map with the name of the actor as key, and the associated class
   *         as value
   */
  private static Map<String, IActor> getActors() {
    Map<String, IActor> map = new LinkedHashMap<String, IActor>();
    // We first create a List with all available actors. We don't create Map
    // directly to don't have to reference each Actor, to get their name,
    // since we cannot put in an interface a static method.
    List<IActor> availableActors = new ArrayList<IActor>();
    availableActors.add(new AgileDeveloperActor());
    availableActors.add(new AgileReviewerActor());
    if (isManualReviewProcessEnabled) {
      availableActors.add(new DeveloperActor());
      availableActors.add(new ReviewerActor());
    }
    if (isUndoHijackModeEnabled) {
      availableActors.add(new UnhijackerActor());
    }
    if (isCheckinCheckerModeEnabled) {
      availableActors.add(new CheckinCheckerActor());
    }
    if (isTesterModeEnabled) {
      availableActors.add(new TestAgileReviewerActor());
    }
    // Then we create the Map
    for (IActor actor : availableActors) {
      map.put(actor.getName(), actor);
    }
    return map;
  }
}
